﻿using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace SBPweb.Forge.Collections
{

    public class ObservableList<TItem> : IList<TItem>, ICollection<TItem>, IEnumerable<TItem>, INotifyPropertyChanged
#if SILVERLIGHT
        , INotifyCollectionChanged
#endif
    {

        #region Private fields

        private List<TItem> items;
        private bool isAutoSortingActive = false;

        #endregion

        #region Constructor logic

        public ObservableList()
        {
            items = new List<TItem>();
        }

        public ObservableList(int capacity)
        {
            items = new List<TItem>(capacity);
        }

        public ObservableList(IEnumerable<TItem> list)
        {
            items = new List<TItem>(list);
        }

        #endregion

        #region Property accessors

        protected List<TItem> ItemsAsList
        {
            get
            {
                return items;
            }
        }

        public int Count
        {
            get
            {
                return items.Count;
            }
        }

        public bool IsReadOnly
        {
            get;
            set;
        }

        public TItem this[int index]
        {
            get
            {
                return items[index];
            }
            set
            {
                items[index] = value;
            }
        }

        #endregion

        #region Events

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Methods

        #region Sorting

        public virtual void Sort()
        {
            items.Sort();
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Reset, default(TItem), default(TItem), -1);
        }

        public virtual void Sort(Comparison<TItem> comparison)
        {
            items.Sort(comparison);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Reset, default(TItem), default(TItem), -1);
        }

        public virtual void Sort(IComparer<TItem> comparer)
        {
            items.Sort(comparer);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Reset, default(TItem), default(TItem), -1);
        }

        public virtual void Sort(int index, int count, IComparer<TItem> comparer)
        {
            items.Sort(index, count, comparer);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Reset, default(TItem), default(TItem), -1);
        }

        #endregion

        #region Event handling

        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
        }

        protected void RaisePropertyChangedEvent(string propertyName)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
            OnPropertyChanged(e);
        }

        protected void RaiseCollectionChangedEvent(NotifyCollectionChangedAction action, TItem newItem, TItem oldItem, int index)
        {
            RaiseCollectionChangedEvent(action, new List<TItem>() { newItem }, new List<TItem>() { oldItem }, index);
        }

        protected void RaiseCollectionChangedEvent(NotifyCollectionChangedAction action, List<TItem> newItems, List<TItem> oldItems, int index)
        {
            NotifyCollectionChangedEventArgs nccea = null;
            PropertyChangedEventArgs pcea = null;
            switch (action)
            {
                default:
                    break;
                case NotifyCollectionChangedAction.Add:
                    if (newItems.Count > 0)
                    {
                        nccea = new NotifyCollectionChangedEventArgs(action, newItems[0], index);
                    }
                    pcea = new PropertyChangedEventArgs("Count");
                    break;
                case NotifyCollectionChangedAction.Replace:
                    nccea = new NotifyCollectionChangedEventArgs(action, oldItems[0], newItems[0], index);
                    //nccea.OldItems.Add(oldItems[0]);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    nccea = new NotifyCollectionChangedEventArgs(action, oldItems[0], index);
                    pcea = new PropertyChangedEventArgs("Count");
                    break;
                case NotifyCollectionChangedAction.Reset:
                    nccea = new NotifyCollectionChangedEventArgs(action);
                    pcea = new PropertyChangedEventArgs("Count");
                    break;
            }
            if (CollectionChanged != null && nccea != null)
            {
                CollectionChanged(this, nccea);
            }
            if (nccea != null)
            {
                OnCollectionChanged(nccea);
            }

            if (PropertyChanged != null && pcea != null)
            {
                PropertyChanged(this, pcea);
            }
            if (pcea != null)
            {
                OnPropertyChanged(pcea);
            }
        }

        #endregion

        #region List access

        public int IndexOf(TItem item)
        {
            return items.IndexOf(item);
        }

        public void Insert(int index, TItem item)
        {
            InsertItem(index, item);
        }

        public void RemoveAt(int index)
        {
            TItem item = this[index];
            RemoveItem(item);
        }

        public void Add(TItem item)
        {
            AddItem(item);
        }

        public void Clear()
        {
            ClearItems();
        }

        public bool Contains(TItem item)
        {
            return items.Contains(item);
        }

        public void CopyTo(TItem[] array, int arrayIndex)
        {
            items.CopyTo(array, arrayIndex);
        }

        public bool Remove(TItem item)
        {
            if (!items.Contains(item))
            {
                return false;
            }
            RemoveItem(item);
            return true;
        }

        public IEnumerator<TItem> GetEnumerator()
        {
            return items.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return items.GetEnumerator();
        }

        #endregion

        #region Collection access

        protected virtual void AddItem(TItem item)
        {
            items.Add(item);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Add, item, default(TItem), items.Count - 1);
        }

        protected virtual void ClearItems()
        {
            items.Clear();
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Reset, default(TItem), default(TItem), -1);
        }

        protected virtual void InsertItem(int index, TItem item)
        {
            items.Insert(index, item);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Add, item, default(TItem), index);
        }

        protected virtual void RemoveItem(TItem item)
        {
            int index = items.IndexOf(item);
            items.RemoveAt(index);
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Remove, default(TItem), item, index);
        }

        protected virtual void SetItem(int index, TItem item)
        {
            items[index] = item;
            RaiseCollectionChangedEvent(NotifyCollectionChangedAction.Replace, item, default(TItem), index);
        }

        public TItem[] ToArray()
        {
            return items.ToArray();
        }

        #endregion

#if SILVERLIGHT
        public static implicit operator ObservableList<TItem>(ObservableCollection<TItem> collection)
        {
            return new ObservableList<TItem>(collection);
        }
#endif

        #endregion

    }

}

#if !SILVERLIGHT
namespace System.Collections.Specialized
{
    // Summary:
    //     Notifies listeners of dynamic changes to a collection, such as when items
    //     are added and removed, or the entire collection object is reset.
    public interface INotifyCollectionChanged
    {
        // Summary:
        //     Occurs when the items list of the collection has changed, or the collection
        //     is reset.
        event NotifyCollectionChangedEventHandler CollectionChanged;
    }

    // Summary:
    //     Represents the method that handles events that implement the System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged
    //     event.
    //
    // Parameters:
    //   sender:
    //     The object that raised the event.
    //
    //   e:
    //     The event data.
    public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e);
    // Summary:
    //     Provides event data for the System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged
    //     event.

    // Summary:
    //     Describes the action that caused a System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged
    //     event.
    public enum NotifyCollectionChangedAction
    {
        // Summary:
        //     One or more items were added to the collection.
        Add = 0,
        //
        // Summary:
        //     One or more items were removed from the collection.
        Remove = 1,
        //
        // Summary:
        //     One or more items were replaced in the collection.
        Replace = 2,
        //
        // Summary:
        //     The content of the collection changed dramatically.
        Reset = 4,
    }

    public sealed class NotifyCollectionChangedEventArgs : EventArgs
    {

        private NotifyCollectionChangedAction action;
        private IList newItems;
        private IList oldItems;
        private int newStartingIndex = -1;
        private int oldStartingIndex = -1;

        // Summary:
        //     Initializes a new instance of the System.Collections.Specialized.NotifyCollectionChangedEventArgs
        //     class, specifying the System.Collections.Specialized.NotifyCollectionChangedAction
        //     value.
        //
        // Parameters:
        //   action:
        //     The action value, as a value of the enumeration. This should always be System.Collections.Specialized.NotifyCollectionChangedAction.Reset;
        //     see Remarks.
        //
        // Exceptions:
        //   System.NotSupportedException:
        //     action is not System.Collections.Specialized.NotifyCollectionChangedAction.Reset.
        public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action)
        {
            if (action != NotifyCollectionChangedAction.Reset)
            {
                throw new NotSupportedException();
            }

            this.action = action;
            this.newItems = new List<object>();
            this.oldItems = new List<object>();
        }

        //
        // Summary:
        //     Initializes a new instance of the System.Collections.Specialized.NotifyCollectionChangedEventArgs
        //     class that describes an System.Collections.Specialized.NotifyCollectionChangedAction.Add
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove change.
        //
        // Parameters:
        //   action:
        //     The action value, as a value of the enumeration. This should be System.Collections.Specialized.NotifyCollectionChangedAction.Add
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove; see
        //     Remarks.
        //
        //   changedItem:
        //     The item that is affected by the change.
        //
        //   index:
        //     The index where the change occurred.
        //
        // Exceptions:
        //   System.NotSupportedException:
        //     action is not System.Collections.Specialized.NotifyCollectionChangedAction.Add
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove.
        public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, int index)
        {
            if (action != NotifyCollectionChangedAction.Add || action != NotifyCollectionChangedAction.Remove)
            {
                throw new NotSupportedException();
            }

            this.action = action;
            this.newItems = new List<object>();
            this.oldItems = new List<object>();
            if (changedItem != null)
            {
                this.newItems.Add(changedItem);
            }
            newStartingIndex = index;
        }
        //
        // Summary:
        //     Initializes a new instance of the System.Collections.Specialized.NotifyCollectionChangedEventArgs
        //     class that describes a System.Collections.Specialized.NotifyCollectionChangedAction.Replace
        //     change.
        //
        // Parameters:
        //   action:
        //     The action value, as a value of the enumeration. This should be System.Collections.Specialized.NotifyCollectionChangedAction.Replace;
        //     see Remarks.
        //
        //   newItem:
        //     The new item that is replacing the original item.
        //
        //   oldItem:
        //     The original item that is replaced.
        //
        //   index:
        //     The index of the item being replaced.
        //
        // Exceptions:
        //   System.NotSupportedException:
        //     action is not System.Collections.Specialized.NotifyCollectionChangedAction.Replace.
        public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem, int index)
        {
            if (action != NotifyCollectionChangedAction.Replace)
            {
                throw new NotSupportedException();
            }

            this.action = action;
            this.newItems = new List<object>();
            this.oldItems = new List<object>();
            if (newItem != null)
            {
                this.newItems.Add(newItem);
            }
            if (oldItem != null)
            {
                this.oldItems.Add(oldItem);
            }
            newStartingIndex = index;
        }

        // Summary:
        //     Gets the description of the action that caused the event.
        //
        // Returns:
        //     The description of the action that caused the event, as a value of the enumeration.
        public NotifyCollectionChangedAction Action
        {
            get
            {
                return action;
            }
        }
        //
        // Summary:
        //     Gets the items affected by an action.
        //
        // Returns:
        //     The list of items affected by an action. The default is null.
        public IList NewItems
        {
            get
            {
                return newItems;
            }
        }
        //
        // Summary:
        //     Gets the index at which the change occurred.
        //
        // Returns:
        //     The index at which the change occurred.
        public int NewStartingIndex
        {
            get
            {
                return newStartingIndex;
            }
        }
        //
        // Summary:
        //     Gets the item affected by a System.Collections.Specialized.NotifyCollectionChangedAction.Replace
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove action.
        //
        // Returns:
        //     The list of items affected by a System.Collections.Specialized.NotifyCollectionChangedAction.Replace
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove action.
        public IList OldItems
        {
            get
            {
                return oldItems;
            }
        }
        //
        // Summary:
        //     Gets the index at which the change occurred for a System.Collections.Specialized.NotifyCollectionChangedAction.Replace
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove action.
        //
        // Returns:
        //     The index at which the change occurred for a System.Collections.Specialized.NotifyCollectionChangedAction.Replace
        //     or System.Collections.Specialized.NotifyCollectionChangedAction.Remove action.
        public int OldStartingIndex
        {
            get
            {
                return oldStartingIndex;
            }
        }

    }

}
#endif